package middleware

import (
	"context"
	"fmt"
	"github.com/chicken-team-outside/chicken_transmission/proxy_server/setting"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"time"
)

const TokenKey = "token"
const UserIdKey = "user_id"
const RpcInfoKey = "rpc_info"

func ClearToken(ctx *gin.Context) {
	ctx.SetCookie(TokenKey, "", -1, "/", "", false, true)
}

func ClearTokenByUser(ctx context.Context, userId uint64) {
	id := fmt.Sprintf("%d", userId)
	token, err := setting.RDB.Get(ctx, id).Result()
	if err == nil {
		if err := setting.RDB.Del(ctx, token).Err(); err != nil {
			zap.L().Warn("failed to delete user token id cache", zap.Error(err), zap.String("key", id))
		}
		if err := setting.RDB.Del(ctx, id).Err(); err != nil {
			zap.L().Warn("failed to delete token cache", zap.Error(err), zap.String("key", id))
		}
	}
}

func getTokenFormCtx(ctx *gin.Context) string {
	token := ctx.Request.Header.Get(TokenKey)
	var err error
	if token != "" {
		if _, err = uuid.Parse(token); err == nil {
			return token
		}
	}
	if token, err = ctx.Cookie(TokenKey); err == nil {
		if _, err = uuid.Parse(token); err == nil {
			return token
		}
	}
	return ""
}

func CookieVerify(ctx *gin.Context) bool {
	token := getTokenFormCtx(ctx)
	if token == "" {
		return false
	}
	userId, err := setting.RDB.Get(ctx, token).Uint64()
	if err != nil {
		return false
	}
	ctx.Set(UserIdKey, userId)
	return true
}

func SetLoginToken(ctx *gin.Context, userId uint64) (string, error) {
	token := uuid.NewString()
	expires := time.Duration(setting.CookieMaxAge) * time.Second
	if err := setting.RDB.Set(ctx, token, userId, expires).Err(); err != nil {
		return "", err
	}
	if err := setting.RDB.Set(ctx, fmt.Sprintf("%d", userId), token, expires).Err(); err != nil {
		if err1 := setting.RDB.Del(ctx, token).Err(); err != nil {
			zap.L().Warn("failed to delete token from cache when set token occur err", zap.Error(err1), zap.String("token", token))
		}
		return "", err
	}
	ctx.SetCookie(TokenKey, token, setting.CookieMaxAge, "/", "", false, true)
	return token, nil
}

func GetUserId(ctx *gin.Context) (uint64, bool) {
	value, exists := ctx.Get(UserIdKey)
	if !exists || value == nil {
		return 0, false
	}
	return value.(uint64), true
}

func AuthWare(ctx *gin.Context) {
	if CookieVerify(ctx) {
		ctx.Next()
	} else {
		ctx.AbortWithStatusJSON(200, gin.H{
			"code": 403,
			"msg":  "please login!",
		})
	}
}
